package top.chmin.project.cloud.builder;

import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import top.chmin.project.cloud.common.entity.auto.Entity;
import top.chmin.project.cloud.common.entity.auto.EntityDao;
import top.chmin.project.cloud.common.entity.column.auto.EntityColumn;
import top.chmin.project.cloud.common.entity.column.auto.EntityColumnDao;
import top.chmin.project.cloud.common.entity.label.value.auto.EntityLabelValue;
import top.chmin.project.cloud.common.entity.label.value.auto.EntityLabelValueDao;

/**
 * chmin
 * 2023/12/15 15:21
 * cloud-builder
 */
@Component
public class ServiceBuilder implements Builder {

    @Resource
    EntityDao entityDao;
    @Resource
    EntityColumnDao entityColumnDao;
    @Resource
    EntityLabelValueDao entityLabelValueDao;

    @Override
    public void build(BuilderConfig config) {
        String content = """
                package top.chmin.project.cloud.#e.module#.#e.pack#.auto;
                                
                // field import auto
                                
                import cn.hutool.core.bean.BeanUtil;
                import jakarta.annotation.Resource;
                import org.springframework.stereotype.Service;
                import org.springframework.transaction.annotation.Transactional;
                import top.chmin.project.cloud.common.entity.Page;
                                
                #import#
                                
                /**
                 * chmin
                 * #dateTime#
                 * cloud-builder
                 */
                @Service
                @Transactional(rollbackFor = Exception.class)
                public class #e.simpleName#Service {
                                
                    @Resource
                    #e.simpleName#Plugin plugin;
                    
                    @Resource
                    #e.simpleName#Dao #e.fieldName#Dao;
                                
                // field labelDao auto
                                
                    public int save(#e.simpleName#Save.Param param) {
                        plugin.checkSave(param);
                        #e.simpleName# #e.fieldName# = BeanUtil.toBean(param, #e.simpleName#.class);
                        plugin.beforeSave(#e.fieldName#);
                        int result = #e.fieldName#Dao.save(#e.fieldName#);
                        plugin.afterSave(param, #e.fieldName#, result);
                        return result;
                    }
                    
                    public int updateById(#e.simpleName#UpdateById.Param param) {
                        plugin.checkUpdateById(param);
                        #e.simpleName# #e.fieldName# = BeanUtil.toBean(param, #e.simpleName#.class);
                        plugin.beforeUpdateById(#e.fieldName#);
                        int result = #e.fieldName#Dao.updateById(#e.fieldName#);
                        plugin.afterUpdateById(param, #e.fieldName#, result);
                        return result;
                    }
                                
                    public int deleteById(#e.simpleName#DeleteById.Param param) {
                        plugin.checkDeleteById(param);
                        int result = #e.fieldName#Dao.deleteById(param.getId());
                        plugin.afterDeleteById(param, result);
                        return result;
                    }
                                
                    public #e.simpleName#SelectById.Result selectById(#e.simpleName#SelectById.Param param) {
                        plugin.checkSelectById(param);
                        #e.simpleName#FPC fpc = new #e.simpleName#FPC().id().eq(param.getId());
                        plugin.beforeSelectById(param, fpc);
                        #e.simpleName#SelectById.Result result = #e.fieldName#Dao.getFirst(fpc.toCondition(), #e.simpleName#SelectById.Result.class);
                        plugin.afterSelectById(param, result);
                        return result;
                    }
                                
                    public Page<#e.simpleName#SelectPage.Result> selectPage(#e.simpleName#SelectPage.Param param) {
                        plugin.checkSelectPage(param);
                        #e.simpleName#FPC fpc = new #e.simpleName#FPC();
                        fpc = param.isDesc()? fpc.id().desc(): fpc.id().asc();
                        plugin.beforeSelectPage(param, fpc);
                        Page<#e.simpleName#SelectPage.Result> result = #e.fieldName#Dao.getPageByCondition(fpc.toCondition(), #e.simpleName#SelectPage.Result.class);
                // field label auto
                        plugin.afterSelectPage(param, result);
                        return result;
                    }
                                
                // entity select auto
                                
                // entity search auto
                                
                }
                """;

        save(config, config.getEntity().getClassName() + "Service", content, true);

        for (EntityLabelValue entityLabelValue : config.getEntityLabelValueList()) {
            EntityColumn label = config.getEntityColumnList()
                    .stream().filter(f -> f.getFieldCode().equals(entityLabelValue.getLabelFieldCode()))
                    .findFirst().orElse(null);
            EntityColumn value = config.getEntityColumnList()
                    .stream().filter(f -> f.getFieldCode().equals(entityLabelValue.getValueFieldCode()))
                    .findFirst().orElse(null);
            if (label == null || value == null) {
                continue;
            }

            if ("SELECT".equals(entityLabelValue.getDataApiType())) {
                config.getEntityColumnList()
                        .forEach(f -> appendJava(config, f,
                                "// field import auto",
                                "import java.util.List;",
                                "import java.util.List;",
                                config.getEntity().getClassName() + "Service"));
                config.getEntityColumnList()
                        .forEach(f -> appendJava(config, f,
                                "// field import auto",
                                "import java.util.stream.Stream;",
                                "import java.util.stream.Stream;",
                                config.getEntity().getClassName() + "Service"));
                appendJava(config,
                        "// entity select auto",
                        "List<#e.simpleName#%sSelect.Result> select%s(#e.simpleName#%sSelect.Param param)".formatted(
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName()),
                        """
                                    // entity select auto
                                    public List<#e.simpleName#%sSelect.Result> select%s(#e.simpleName#%sSelect.Param param) {
                                        plugin.checkSelect%s(param);
                                        #e.simpleName#FPC fpc = new #e.simpleName#FPC();
                                        plugin.beforeSelect%s(param, fpc);
                                        Stream<#e.simpleName#%sSelect.Result> result = #e.fieldName#Dao.getStreamByCondition(fpc.toCondition(), #e.simpleName#%sSelect.Result.class)
                                            .peek(r -> {
                                                r.setValue(r.get%s());
                                                r.setLabel(r.get%s());
                                            });
                                        List<#e.simpleName#%sSelect.Result> list = result.toList();
                                        plugin.afterSelect%s(param, list);
                                        return list;
                                    }
                                """.formatted(
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                label.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName()
                        ),
                        config.getEntity().getClassName() + "Service");
            }

            if ("SEARCH".equals(entityLabelValue.getDataApiType())) {
                config.getEntityColumnList()
                        .forEach(f -> appendJava(config, f,
                                "// field import auto",
                                "import java.util.List;",
                                "import java.util.List;",
                                config.getEntity().getClassName() + "Service"));
                config.getEntityColumnList()
                        .forEach(f -> appendJava(config, f,
                                "// field import auto",
                                "import java.util.stream.Stream;",
                                "import java.util.stream.Stream;",
                                config.getEntity().getClassName() + "Service"));
                appendJava(config,
                        "// entity search auto",
                        "List<#e.simpleName#%sSearch.Result> search%s(#e.simpleName#%sSearch.Param param)".formatted(
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName()),
                        """
                                    // entity search auto
                                    public List<#e.simpleName#%sSearch.Result> search%s(#e.simpleName#%sSearch.Param param) {
                                        plugin.checkSearch%s(param);
                                        #e.simpleName#FPC fpc = new #e.simpleName#FPC();
                                        fpc.limit(10);
                                        plugin.beforeSearch%s(param, fpc);
                                        Stream<#e.simpleName#%sSearch.Result> result = #e.fieldName#Dao.getStreamByCondition(fpc.toCondition(), #e.simpleName#%sSearch.Result.class)
                                            .peek(r -> {
                                                r.setValue(r.get%s());
                                                r.setLabel(r.get%s());
                                            });
                                        List<#e.simpleName#%sSearch.Result> list = result.toList();
                                        plugin.afterSearch%s(param, list);
                                        return list;
                                    }
                                """.formatted(
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName(),
                                label.getMethodName(),
                                value.getMethodName(),
                                value.getMethodName()
                        ),
                        config.getEntity().getClassName() + "Service");
            }

        }

        config.getEntityColumnRelationList().forEach(r -> {
            EntityColumn column = config.getEntityColumnList().stream().filter(f -> f.getFieldCode().equals(r.getSourceFieldCode())).findFirst().orElseThrow();
            Entity targetEntity = getEntity(entityDao, r.getTargetTableCode());
            EntityColumn targetValue = getEntityColumn(entityColumnDao, r.getTargetFieldCode());
            EntityLabelValue entityLabelValue = getEntityLabelValue(entityLabelValueDao, r.getTargetTableCode())
                    .stream().filter(e -> e.getValueFieldCode().equals(r.getTargetFieldCode())).findFirst().orElseThrow();
            EntityColumn targetLabel = getEntityColumn(entityColumnDao, entityLabelValue.getLabelFieldCode());

            appendJava(
                    config,
                    column,
                    "// field import auto",
                    "import top.chmin.project.cloud.%s.%s.auto.%s;".formatted(targetEntity.getModule(), targetEntity.getPack(), targetEntity.getClassName()),
                    "import top.chmin.project.cloud.%s.%s.auto.%s;".formatted(targetEntity.getModule(), targetEntity.getPack(), targetEntity.getClassName()),
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field import auto",
                    "import top.chmin.project.cloud.%s.%s.auto.%sDao;".formatted(targetEntity.getModule(), targetEntity.getPack(), targetEntity.getClassName()),
                    "import top.chmin.project.cloud.%s.%s.auto.%sDao;".formatted(targetEntity.getModule(), targetEntity.getPack(), targetEntity.getClassName()),
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field import auto",
                    "import top.chmin.project.cloud.%s.%s.auto.%sFPC;".formatted(targetEntity.getModule(), targetEntity.getPack(), targetEntity.getClassName()),
                    "import top.chmin.project.cloud.%s.%s.auto.%sFPC;".formatted(targetEntity.getModule(), targetEntity.getPack(), targetEntity.getClassName()),
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field labelDao auto",
                    "%sDao %sDao;".formatted(targetEntity.getClassName(), StringUtils.uncapitalize(targetEntity.getClassName())),
                    "@Resource\n    %sDao %sDao;".formatted(targetEntity.getClassName(), StringUtils.uncapitalize(targetEntity.getClassName())),
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field import auto",
                    "import java.util.List;",
                    "import java.util.List;",
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field import auto",
                    "import java.util.Map;",
                    "import java.util.Map;",
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field import auto",
                    "import java.util.stream.Collectors;",
                    "import java.util.stream.Collectors;",
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field import auto",
                    "import cn.hutool.core.collection.CollectionUtil;",
                    "import cn.hutool.core.collection.CollectionUtil;",
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field import auto",
                    "import java.util.stream.Stream;",
                    "import java.util.stream.Stream;",
                    config.getEntity().getClassName() + "Service"
            );

            appendJava(
                    config,
                    column,
                    "// field label auto",
                    "r.set%s(%s == null ? null : %s.get%s());".formatted(
                            targetLabel.getMethodName(),
                            StringUtils.uncapitalize(targetEntity.getClassName()),
                            StringUtils.uncapitalize(targetEntity.getClassName()),
                            targetLabel.getMethodName()),
                    """
                                if (CollectionUtil.isNotEmpty(result.getList())) {
                                    List<%s> %sList = result.getList().stream()
                                            .map(#e.simpleName#SelectPage.Result::get#f.methodName#)
                                            .toList();
                                    Stream<%s> %sStream = %sDao.getStreamByCondition(
                                            new %sFPC()
                                                    .forDistinct()
                                                    .%s().in(%sList)
                                                    .toCondition()
                                    );
                                    Map<%s, %s> %sMap = %sStream.collect(Collectors.toMap(
                                            %s::get%s,
                                            t -> t
                                    ));
                                    result.getList().forEach(r -> {
                                        %s %s = %sMap.get(r.get#f.methodName#());
                                        r.set%sLabel(%s == null ? null : %s.get%s());
                                    });
                            }""".formatted(
                            targetValue.getSimpleType(),
                            column.getFieldName(),

                            targetEntity.getClassName(),
                            column.getFieldName(),
                            StringUtils.uncapitalize(targetEntity.getClassName()),
                            targetEntity.getClassName(),
                            targetValue.getFieldName(),
                            column.getFieldName(),

                            targetValue.getSimpleType(),
                            targetEntity.getClassName(),
                            column.getFieldName(),
                            column.getFieldName(),
                            targetEntity.getClassName(),
                            targetValue.getMethodName(),

                            targetEntity.getClassName(),
                            StringUtils.uncapitalize(targetEntity.getClassName()),
                            column.getFieldName(),
                            column.getMethodName(),
                            StringUtils.uncapitalize(targetEntity.getClassName()),
                            StringUtils.uncapitalize(targetEntity.getClassName()),
                            targetLabel.getMethodName()
                    ),
                    config.getEntity().getClassName() + "Service"
            );
        });
    }
}
